/**
 * \file OdiohPlaya.cpp
 * \brief Audio Player for Linux 
 * \author Joao Guerreiro
 * \date December 2, 2010 
 */

#include "OdiohPlaya.h"

/**
 * \brief Constructor
 *
 * Constructor of the OdiohPlay class
 */
OdiohPlaya::OdiohPlaya() {
    widget.setupUi(this);

    //no resizing allowed
    this->setFixedSize(this->size());
    //if play is not started
    currentTrack = -1;

    //setting the icons for all the QPushButtons

    widget.nextButton->setIcon(style()->standardIcon(QGtkStyle::SP_MediaSkipForward));
    widget.previousButton->setIcon(style()->standardIcon(QGtkStyle::SP_MediaSkipBackward));
    widget.playPauseButton->setIcon(style()->standardIcon(QGtkStyle::SP_MediaPlay));
    widget.stopButton->setIcon(style()->standardIcon(QGtkStyle::SP_MediaStop));
    widget.removeTracksButton->setIcon(style()->standardIcon(QGtkStyle::SP_TrashIcon));
    widget.clearTrackListButton->setIcon(style()->standardIcon(QGtkStyle::SP_BrowserReload));
    widget.addTracksButton->setIcon(style()->standardIcon(QGtkStyle::SP_DialogOpenButton));

    //setting the tooltip for every QPushButton

    widget.nextButton->setToolTip("Next Track");
    widget.previousButton->setToolTip("Previous Track");
    widget.playPauseButton->setToolTip("Play/Pause");
    widget.stopButton->setToolTip("Stop");
    widget.removeTracksButton->setToolTip("Delete Selected Track(s)");
    widget.clearTrackListButton->setToolTip("Clear List");
    widget.addTracksButton->setToolTip("Add Track(s)");


    audioOutput = new Phonon::AudioOutput(Phonon::MusicCategory, this);
    mediaObject = new Phonon::MediaObject(this);

    //Connecting signals to slots

    connect(widget.nextButton, SIGNAL(pressed()), this, SLOT(nextTrack()));
    connect(widget.previousButton, SIGNAL(pressed()), this, SLOT(previousTrack()));
    connect(widget.addTracksButton, SIGNAL(pressed()), this, SLOT(addTracks()));
    connect(mediaObject, SIGNAL(totalTimeChanged(qint64)), this, SLOT(updateTime()));
    connect(mediaObject, SIGNAL(tick(qint64)), this, SLOT(updateTime()));
    connect(mediaObject, SIGNAL(finished()), this, SLOT(trackFinished()));
    connect(widget.playPauseButton, SIGNAL(pressed()), this, SLOT(playPause()));
    connect(widget.stopButton, SIGNAL(pressed()), this, SLOT(stopPlayback()));
    connect(widget.trackTable, SIGNAL(cellDoubleClicked(int, int)),
            this, SLOT(rowDoubleClicked(int, int)));
    connect(widget.removeTracksButton, SIGNAL(pressed()), this, SLOT(removeTracks()));
    connect(widget.clearTrackListButton, SIGNAL(pressed()), this, SLOT(clearTrackList()));
    connect(widget.volumeController, SIGNAL(valueChanged(int)), this, SLOT(changeVolume(int)));


    //Connecting source and sink
    Phonon::createPath(mediaObject, audioOutput);

    //Setting the max and min values for the volume controller
    widget.volumeController->setMaximum(100);
    widget.volumeController->setMinimum(0);

    //Making the volume controller's notches visible
    //by steps of 5
    widget.volumeController->setNotchesVisible(true);
    widget.volumeController->setNotchTarget(5.0);

    //Set the initial volume at 75%
    audioOutput->setVolume(0.75);
    widget.volumeController->setValue(75);

    //Connecting the Seek Slider to the media object
    widget.trackSeekSlider->setMediaObject(mediaObject);

    //Enabling drops on the QTableWidget
    this->setAcceptDrops(true);


}

/**
 * \brief Destructor
 *
 * Destructor of the OdiohPlay class
 */
OdiohPlaya::~OdiohPlaya() {
}

/**
 * \brief Adding tracks to the playlist
 *
 * Adding tracks to the playlist by pressing a QPushButton
 */
void OdiohPlaya::addTracks() {

    bool emptyTrackList = (widget.trackTable->rowCount() == 0);


    QStringList files = QFileDialog::getOpenFileNames(this, tr("Select Music Files"),
            QDesktopServices::storageLocation(QDesktopServices::MusicLocation));

    //if no file was selected
    if (files.isEmpty())
        return;



    //go through all the selected files and add them to the sources and the table
    QList<QString>::iterator trackIterator;
    int i = 0;
    for (trackIterator = files.begin(); trackIterator != files.end(); trackIterator++) {

        sources.append(*trackIterator);
        widget.trackTable->insertRow(widget.trackTable->rowCount());
        QTableWidgetItem *trackName = new QTableWidgetItem();
        trackName->setText(files.at(i));
        trackName->setFlags(trackName->flags() ^ Qt::ItemIsEditable);
        widget.trackTable->setItem(widget.trackTable->rowCount() - 1, 0, trackName);

        i++;


    }

    //if the playlist was previously empty, then we start playing the first track
    //otherwise we just added more tracks to the playlist and continue playing the
    //current track
    if (emptyTrackList) {

        currentTrack = 0;
        mediaObject->setCurrentSource(sources.at(currentTrack));
        widget.trackTable->selectRow(currentTrack);
        mediaObject->play();
        widget.playPauseButton->setIcon(style()->standardIcon(QGtkStyle::SP_MediaPause));

    }



    widget.trackTable->setColumnWidth(0, widget.trackTable->width());


}

/**
 * \brief Updating the current time
 *
 * Taken from the qmediaplayer example
 */
void OdiohPlaya::updateTime() {

    //taken from the qmediaplayer example

    long length = mediaObject->totalTime();
    long position = mediaObject->currentTime();
    QString currentTimeString;
    QString totalTimeString;
    if (position || length) {
        int sec = position / 1000;
        int min = sec / 60;
        int hour = min / 60;
        int msec = position;

        QTime playTime(hour % 60, min % 60, sec % 60, msec % 1000);
        sec = length / 1000;
        min = sec / 60;
        hour = min / 60;
        msec = length;

        QTime stopTime(hour % 60, min % 60, sec % 60, msec % 1000);

        QString timeFormat = "m:ss";

        if (hour > 0)
            timeFormat = "h:mm:ss";

        currentTimeString = playTime.toString(timeFormat);

        if (length) {

            totalTimeString = stopTime.toString(timeFormat);
            widget.totalTime->setText(totalTimeString);

        }
    }
    widget.currentTime->setText(currentTimeString);

}

/**
 * \brief Changing to the next track
 *
 * Sets the current track to the next track
 */
void OdiohPlaya::nextTrack() {


    //if not empty track list
    if (currentTrack != -1) {

        //increase the current track number
        currentTrack = (currentTrack + 1) % (widget.trackTable->rowCount());

        //update the source
        mediaObject->setCurrentSource(sources.at(currentTrack));

        mediaObject->play();
        widget.playPauseButton->setIcon(style()->standardIcon(QGtkStyle::SP_MediaPause));
        widget.trackTable->selectRow(currentTrack);

    }

}

/**
 * \brief Changing to the previous track
 *
 * Sets the current track to the previous track
 */
void OdiohPlaya::previousTrack() {

    //if the playlist is not empty
    if (currentTrack != -1) {

        currentTrack = currentTrack - 1;

        if (currentTrack == -1)
            currentTrack = widget.trackTable->rowCount() - 1;

        mediaObject->setCurrentSource(sources.at(currentTrack));

        mediaObject->play();
        widget.playPauseButton->setIcon(style()->standardIcon(QGtkStyle::SP_MediaPause));
        widget.trackTable->selectRow(currentTrack);

    }



}

/**
 * \brief Detecting the end of a track
 *
 * Either stop the playback if the are no more tracks to play,
 * or update the current track to the next one
 */
void OdiohPlaya::trackFinished() {

    //If current track is the last we stop the playback at its end
    if ((currentTrack + 1) % (widget.trackTable->rowCount()) == 0) {

        mediaObject->stop();
    }
    else {

        //updte the current track to the next one
        currentTrack = currentTrack + 1;
        //update the source
        mediaObject->setCurrentSource(sources.at(currentTrack));
        //play the new source
        mediaObject->play();
    }




}

/**
 * \brief Play/Pause the current track
 *
 * Changes the icon of the QPushButton according to the current state
 *
 */
void OdiohPlaya::playPause() {

    if (currentTrack != -1) {

        switch (mediaObject->state()) {

                //if the track was stoped, then we start playing at the current track
            case Phonon::StoppedState:

                mediaObject->play();
                widget.playPauseButton->setIcon(style()->standardIcon(QGtkStyle::SP_MediaPause));

                break;

                //if the track was playing, we pause it and change the icon
            case Phonon::PlayingState:


                mediaObject->pause();
                widget.playPauseButton->setIcon(style()->standardIcon(QGtkStyle::SP_MediaPlay));


                break;

                //if the track is paused, by pressing the button, we resume and change the icon
            case Phonon::PausedState:


                widget.playPauseButton->setIcon(style()->standardIcon(QGtkStyle::SP_MediaPause));
                mediaObject->play();

                break;

        }

    }

}

/**
 * \brief Stop the playback
 *
 * Stops the playback and changes the Play/Pause icon
 */
void OdiohPlaya::stopPlayback() {

    mediaObject->stop();
    widget.playPauseButton->setIcon(style()->standardIcon(QGtkStyle::SP_MediaPlay));

}

/**
 * \brief Detecting a track has been doubleclicked
 *
 * Switches the current track to the one which was doubleclicked
 */
void OdiohPlaya::rowDoubleClicked(int row, int) {

    mediaObject->setCurrentSource(sources.at(row));
    currentTrack = row;
    mediaObject->play();
    widget.playPauseButton->setIcon(style()->standardIcon(QGtkStyle::SP_MediaPause));

}

/**
 * \brief Removing tracks from the playlist
 *
 * Removing all the tracks selected by the user
 */
void OdiohPlaya::removeTracks() {

    //if all the tracks were selected, we call the clearTrackList method
    if (widget.trackTable->selectedItems().size() == widget.trackTable->rowCount())
        clearTrackList();

    else {


        QList<QTableWidgetItem*> rowsSelected = widget.trackTable->selectedItems();

        QList<QTableWidgetItem*>::iterator trackIterator;

        int selectedRows[rowsSelected.size()];
        trackIterator = rowsSelected.begin();


        //copy the selected rows indexes to an array
        for (int i = 0; i < rowsSelected.size(); i++) {

            QTableWidgetItem * selected = *trackIterator;
            selectedRows[i] = selected->row();
            trackIterator++;

        }

        //usage of the library algorithm to sort the selected rows
        sort(selectedRows, selectedRows + rowsSelected.size());

        int selectionSize = rowsSelected.size();

        //go through all the indexes
        for (int i = 0; i < selectionSize; i++) {

            bool found = false;

            //until the index is found
            for (int j = 0; found == false; j++) {

                if (selectedRows[i] == j) {

                    found = true;

                    //if it is the last track
                    if (j == widget.trackTable->rowCount() - 1) {


                        if (currentTrack == j) {

                            currentTrack = 0;
                            mediaObject->setCurrentSource(sources.at(currentTrack));
                            mediaObject->play();
                            widget.playPauseButton->setIcon(style()->standardIcon(QGtkStyle::SP_MediaPause));

                        }



                    } else { //not the last track

                        if (currentTrack == j) {

                            mediaObject->setCurrentSource(sources.at(currentTrack + 1));
                            mediaObject->play();
                            widget.playPauseButton->setIcon(style()->standardIcon(QGtkStyle::SP_MediaPause));

                        } else {

                            if (currentTrack > 0)
                                currentTrack--;


                        }


                    }

                    sources.removeAt(j);
                    widget.trackTable->removeRow(j);

                }

            }

            //update the indexes values
            for (int k = i + 1; k < selectionSize; k++) {

                if (selectedRows[k] > 0)
                    selectedRows[k] = selectedRows[k] - 1;
            }



        }

    }
}

/**
 * \brief Clears the whole track list
 *
 * Removes all the songs from the playlist
 */
void OdiohPlaya::clearTrackList() {


    int rowCount = widget.trackTable->rowCount();

    //erase all sources from beginning to end
    sources.erase(sources.begin(), sources.end());

    //remove all rows from the QTableWidget
    for (int i = 0; i < rowCount; i++)
        widget.trackTable->removeRow(widget.trackTable->rowCount() - 1);


    mediaObject->stop();
    mediaObject->clear();
    currentTrack = -1;
    widget.playPauseButton->setIcon(style()->standardIcon(QGtkStyle::SP_MediaPlay));



}

/**
 * \brief Detecting a change of volume
 *
 * Updates the volume to the user provided value
 */
void OdiohPlaya::changeVolume(int currentVolume) {

    audioOutput->setVolume((qreal) currentVolume / 100);

}

/**
 * \brief Acceptance of draging
 *
 */
void OdiohPlaya::dragEnterEvent(QDragEnterEvent *event) {

    event->accept();
}

/**
 * \brief Drop event detection
 *
 * Tracks can be dropped from outside the application onto the widget,
 * in order to be added to the playlist.
 */
void OdiohPlaya::dropEvent(QDropEvent* event) {

    //to check if we drop tracks on an empty playlist
    bool emptyTrackList = (widget.trackTable->rowCount() == 0);

    //data dropped
    const QMimeData *mimeData = event->mimeData();

    if (mimeData->hasUrls()) {

        QList<QUrl> urlList = mimeData->urls();

        //add the tracks to the table and sources
        for (int i = 0; i < urlList.size(); ++i) {

            QString url = urlList.at(i).path();
            sources.append(url);
            widget.trackTable->insertRow(widget.trackTable->rowCount());
            QTableWidgetItem *trackName = new QTableWidgetItem();
            trackName->setText(url);
            trackName->setFlags(trackName->flags() ^ Qt::ItemIsEditable);
            widget.trackTable->setItem(widget.trackTable->rowCount() - 1, 0, trackName);

        }


        //if the playlist was previously empty, then we start playing the first track
        //otherwise we just added more tracks to the playlist and continue playing the
        //current track
        if (emptyTrackList) {

            currentTrack = 0;
            mediaObject->setCurrentSource(sources.at(currentTrack));
            widget.trackTable->selectRow(currentTrack);
            mediaObject->play();
            widget.playPauseButton->setIcon(style()->standardIcon(QGtkStyle::SP_MediaPause));

        }

        //resizing the columns and header
        widget.trackTable->setColumnWidth(0, widget.trackTable->width());
    }
}